﻿using AutoMapper;
using IOA.MES.Common;
using IOA.MES.DataAccess.Entity;
using IOA.MES.DataAccess.Enum;
using IOA.MES.DataAccess.VModel;
using IOA.Utils;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;

namespace IOA.MES.Business
{
    /// <summary>
    /// 分拣管理（错误代码：141001）
    /// </summary>
    public class SortMgr
    {
        private MESContext mesDb = new MESContext("MES");
        private Logger logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// 组件分拣
        /// </summary>
        /// <returns>分件记录</returns>
        public VSortRecord SortComponent(VSortRecord model)
        {
            var component = mesDb.Components.AsNoTracking().FirstOrDefault(_ => _.BarCode == model.BarCode && _.Valid);
            if (component == null)
            {
                throw new MultiLanBizException(102012, model.BarCode);
            }
            var machine = mesDb.Machines.AsNoTracking().FirstOrDefault(_ => _.PkId == model.MachineId && _.Valid);
            if (machine == null)
            {
                throw new MultiLanBizException(119003, model.MachineId.ToString());
            }
            var existRecord = mesDb.SortRecords.AsNoTracking().FirstOrDefault(_ => _.BarCode == component.BarCode && _.Status == SortRecordStatus.Normal && _.Valid);
            if (existRecord != null)
            {
                logger.Warn($"组件回流：条码：{component.BarCode}，堆号：{existRecord.StackNo}，序号：{existRecord.SortNo}");
                Flowback(Mapper.Map<VSortRecord>(existRecord));
            }
            var rules = QueryValidRules(component.WorkshopCode, model.MachineId);
            VSortRule curRule = null;
            foreach (var rule in rules)
            {
                if (rule.OrderNoCheck && !rule.OrderNos.Any(_ => StringUtils.Equals(_?.Trim(), component.OrderNo?.Trim())))
                {
                    continue;
                }
                if (rule.WorkOrderNoCheck && !rule.WorkOrderNos.Any(_ => StringUtils.Equals(_?.Trim(), component.WorkOrderNo?.Trim())))
                {
                    continue;
                }
                if (rule.NameplateModelCheck && !rule.NameplateModels.Any(_ => StringUtils.Equals(_?.Trim(), component.NameplateModel?.Trim())))
                {
                    continue;
                }
                if (rule.PowerLevelCheck && !rule.PowerLevels.Any(_ => component.IVPowerLevel == _))
                {
                    continue;
                }
                if (rule.CurrentLevelCheck && !rule.CurrentLevels.Any(_ => StringUtils.Equals(_?.Trim(), component.IVCurrentLevel?.Trim())))
                {
                    continue;
                }
                if (rule.QLevelCheck && !rule.QLevels.Any(_ => StringUtils.Equals(_?.Trim(), component.QLevel?.Trim())))
                {
                    continue;
                }
                if (rule.ComponentColorCheck && !rule.ComponentColors.Any(_ => StringUtils.Equals(_?.Trim(), component.ComponentColor?.Trim())))
                {
                    continue;
                }
                if (rule.JunBoxSpecsCheck && !rule.JunBoxSpecses.Any(_ => StringUtils.Equals(_?.Trim(), component.JunBoxSpecs?.Trim())))
                {
                    continue;
                }
                if (rule.JunBoxFactoryCheck && !rule.JunBoxFactories.Any(_ => StringUtils.Equals(_?.Trim(), component.JunBoxFactory?.Trim())))
                {
                    continue;
                }
                if (rule.BackPlanSpecsCheck && !rule.BackPlanSpecses.Any(_ => StringUtils.Equals(_?.Trim(), component.BackPlanSpecs?.Trim())))
                {
                    continue;
                }
                if (rule.BackPlanFactoryCheck && !rule.BackPlanFactories.Any(_ => StringUtils.Equals(_?.Trim(), component.BackPlanFactory?.Trim())))
                {
                    continue;
                }
                if (rule.AluFrameSpecsCheck && !rule.AluFrameSpecses.Any(_ => StringUtils.Equals(_?.Trim(), component.AluFrameSpecs?.Trim())))
                {
                    continue;
                }
                if (rule.AluFrameFactoryCheck && !rule.AluFrameFactories.Any(_ => StringUtils.Equals(_?.Trim(), component.AluFrameFactory?.Trim())))
                {
                    continue;
                }
                if (rule.BatterySpecsCheck && !rule.BatterySpecses.Any(_ => StringUtils.Equals(_?.Trim(), component.BatterySpecs?.Trim())))
                {
                    continue;
                }
                if (rule.BatteryFactoryCheck && !rule.BatteryFactories.Any(_ => StringUtils.Equals(_?.Trim(), component.BatteryFactory?.Trim())))
                {
                    continue;
                }
                if (rule.BadReasonCheck && !rule.BadReasons.Any(_ => StringUtils.Equals(_?.Trim(), component.DowngradeCode?.Trim())))
                {
                    continue;
                }
                curRule = rule;
                break;
            }
            if (curRule == null)
            {
                throw new MultiLanBizException(141002, component.BarCode); //没有匹配到堆放规则
            }
            var possibleStack = mesDb.SortStacks.Where(_ => _.MachineId == machine.PkId && _.StackNo == curRule.StackNo
                 && _.Status == SortStackStatus.Sorting && _.Valid).ToList();
            var existStack = mesDb.SortStacks.FirstOrDefault(_ => _.MachineId == machine.PkId && _.StackNo == curRule.StackNo
                && _.Status == SortStackStatus.Sorting && _.Valid);
            if (possibleStack.Count > 1 && possibleStack != null) //不同堆位，堆放要求一样时，先把堆位上数量多的放满
            {
                var sortno = possibleStack[0].SortNo;
                int sortstack = 0;
                for (int i = 0; i < possibleStack.Count - 1; i++)
                {
                    sortno = possibleStack[i].SortNo;
                    if (sortno >= possibleStack[i + 1].SortNo && sortno != possibleStack[i].FullCount)
                    { sortstack = i; }
                    else sortstack = i + 1;
                }
                existStack = possibleStack[sortstack];
            }


            if (existStack == null //拣货中的堆不存在
                || existStack.SortNo >= curRule.FullCount) //拣货中的堆已堆满
            {
                if (existStack != null)
                {
                    existStack.Status = SortStackStatus.Finish;
                    existStack.ModifyBy = model.CreateBy;
                    existStack.ModifyTime = DateTime.Now;
                    mesDb.SaveChanges();
                }

                existStack = new SortStack
                {
                    WorkshopCode = component.WorkshopCode,
                    WorkshopName = component.WorkshopName,
                    LineId = machine.LineId,
                    LineName = machine.LineName,
                    MachineId = machine.PkId,
                    MachineNo = machine.MachineNo,
                    MachineName = machine.MachineName,
                    FullCount = curRule.FullCount,
                    StackNo = curRule.StackNo,
                    SortNo = 1,
                    Status = SortStackStatus.Sorting,
                    CreateBy = model.CreateBy,
                    CreateTime = DateTime.Now
                };
                mesDb.SortStacks.Add(existStack);
                mesDb.SaveChanges();
            }
            else
            {
                existStack.SortNo = existStack.SortNo + 1;
                if (existStack.SortNo >= curRule.FullCount)
                {
                    existStack.Status = SortStackStatus.Finish;
                }
                existStack.ModifyBy = model.CreateBy;
                existStack.ModifyTime = DateTime.Now;
                mesDb.SaveChanges();
            }

            //var config = GetBarCodeFootProtectConfig(component.BarCode);
            var record = new SortRecord
            {
                BarCode = component.BarCode,
                WorkshopCode = component.WorkshopCode,
                WorkshopName = component.WorkshopName,
                OrderNo = component.OrderNo,
                WorkOrderNo = component.WorkOrderNo,
                LineId = machine.LineId,
                LineName = machine.LineName,
                MachineId = machine.PkId,
                MachineNo = machine.MachineNo,
                MachineName = machine.MachineName,
                StackId = existStack.PkId,
                StackNo = existStack.StackNo,
                SortNo = existStack.SortNo,
                //FootProtect = config?.FootProtectSortNoList.Contains(existStack.SortNo),
                //OverTurn = config?.OverTurnSortNoList.Contains(existStack.SortNo),
                RuleId = curRule.PkId,
                ProductCode = component.ProductCode,
                ProductName = component.ProductName,
                ProductSpecs = component.ProductSpecs,
                QLevel = component.QLevel,
                PowerLevel = component.IVPowerLevel,
                NameplateModel = component.NameplateModel,
                FullCount = curRule.FullCount,
                ComponentColor = component.ComponentColor,
                CurrentLevel = component.IVCurrentLevel,
                AluFrameFactory = component.AluFrameFactory,
                AluFrameSpecs = component.AluFrameSpecs,
                BackPlanFactory = component.BackPlanFactory,
                BackPlanSpecs = component.BackPlanSpecs,
                BatteryFactory = component.BatteryFactory,
                BatterySpecs = component.BatterySpecs,
                JunBoxFactory = component.JunBoxFactory,
                JunBoxSpecs = component.JunBoxSpecs,
                BadReason = component.DowngradeCode,
                Sorted = true,
                Status = SortRecordStatus.Normal,
                CreateBy = model.CreateBy,
                CreateTime = DateTime.Now
            };
            mesDb.SortRecords.Add(record);
            mesDb.SaveChanges();

            return Mapper.Map<VSortRecord>(record);
        }

        /// <summary>
        /// 获取拣货记录
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <param name="isSort">是否分拣机调用</param>
        /// <returns>贱货记录</returns>
        public VSortRecord GetSortRecord(string barCode, bool isSort = false)
        {
            var entity = mesDb.SortRecords.AsNoTracking().FirstOrDefault(_ => _.BarCode == barCode && _.Status == SortRecordStatus.Normal && _.Valid);
            return Mapper.Map<VSortRecord>(entity);
        }

        public VSortStack GetSortStack()
        {
            var entity = mesDb.SortStacks.AsNoTracking().FirstOrDefault(_ => _.Valid && _.Status == 0);

            return Mapper.Map<VSortStack>(entity);
        }

        /// <summary>
        /// 查询有效规则
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="machineId">设备ID</param>
        /// <param name="machineId">设备ID/param>
        /// <returns>有效规则列表</returns>
        public List<VSortRule> QueryValidRules(string workshopCode, int machineId)
        {
            var entities = mesDb.SortRules.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.MachineId == machineId
                && _.Status == SortRuleStatus.Valid && _.Valid).OrderBy(_ => _.StackNo).ToList();
            var models = Mapper.Map<List<VSortRule>>(entities);
            var stacks = mesDb.SortStacks.AsNoTracking().Where(_ => _.MachineId == machineId && _.Status == SortStackStatus.Sorting && _.Valid).ToList();

            models.ForEach(model =>
            {
                model.CurSortNo = stacks.FirstOrDefault(_ => _.StackNo == model.StackNo)?.SortNo ?? 0;

                model.OrderNos = string.IsNullOrWhiteSpace(model.OrderNo) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.OrderNo);
                model.WorkOrderNos = string.IsNullOrWhiteSpace(model.WorkOrderNo) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.WorkOrderNo);
                model.NameplateModels = string.IsNullOrWhiteSpace(model.NameplateModel) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.NameplateModel);
                model.PowerLevels = string.IsNullOrWhiteSpace(model.PowerLevel) ? new List<decimal?>() : JsonUtils.Deserialize<List<decimal?>>(model.PowerLevel);
                model.CurrentLevels = string.IsNullOrWhiteSpace(model.CurrentLevel) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.CurrentLevel);
                model.QLevels = string.IsNullOrWhiteSpace(model.QLevel) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.QLevel);
                model.ComponentColors = string.IsNullOrWhiteSpace(model.ComponentColor) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.ComponentColor);
                model.JunBoxSpecses = string.IsNullOrWhiteSpace(model.JunBoxSpecs) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.JunBoxSpecs);
                model.JunBoxFactories = string.IsNullOrWhiteSpace(model.JunBoxFactory) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.JunBoxFactory);
                model.BackPlanSpecses = string.IsNullOrWhiteSpace(model.BackPlanSpecs) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.BackPlanSpecs);
                model.BackPlanFactories = string.IsNullOrWhiteSpace(model.BackPlanFactory) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.BackPlanFactory);
                model.AluFrameSpecses = string.IsNullOrWhiteSpace(model.AluFrameSpecs) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.AluFrameSpecs);
                model.AluFrameFactories = string.IsNullOrWhiteSpace(model.AluFrameFactory) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.AluFrameFactory);
                model.BatterySpecses = string.IsNullOrWhiteSpace(model.BatterySpecs) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.BatterySpecs);
                model.BatteryFactories = string.IsNullOrWhiteSpace(model.BatteryFactory) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.BatteryFactory);
                model.BadReasons = string.IsNullOrWhiteSpace(model.BadReason) ? new List<string>() : JsonUtils.Deserialize<List<string>>(model.BadReason);
            });
            return models;
        }


        /// <summary>
        /// 查询车间
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="machineId">设备ID/param>
        /// <returns>有效规则列表</returns>
        public List<VSortStackItem> QueryDisplayValidRules(string workshopCode, int machineId)
        {
            var rules = QueryValidRules(workshopCode, machineId);
            return ConvertStacks(rules);
        }

        /// <summary>
        /// 获取展示用堆列表
        /// </summary>
        /// <param name="rules">规则列表</param>
        /// <returns>转换规则</returns>
        public List<VSortStackItem> ConvertStacks(List<VSortRule> rules)
        {
            var stacks = new List<VSortStackItem>();
            foreach (var rule in rules)
            {
                var stack = new VSortStackItem
                {
                    StackNo = rule.StackNo,
                    FullCount = rule.FullCount,
                    WorkshopCode = rule.WorkshopCode,
                    MachineId = rule.MachineId,
                    MachineNo = rule.MachineNo,
                    SortNo = rule.CurSortNo
                };
                if (rule.OrderNoCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("订单", string.Join("，", rule.OrderNos.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _).Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.WorkOrderNoCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("工单", string.Join("，", rule.WorkOrderNos.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.NameplateModelCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("铭牌", string.Join("，", rule.NameplateModels.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.PowerLevelCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("功率", string.Join("，", rule.PowerLevels.Select(_ => !_.HasValue ? "空" : _.ToString()))));
                }
                if (rule.CurrentLevelCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("电流", string.Join("，", rule.CurrentLevels.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.QLevelCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("等级", string.Join("，", rule.QLevels.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.ComponentColorCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("颜色", string.Join("，", rule.ComponentColors.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.JunBoxSpecsCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("线盒规格", string.Join("，", rule.JunBoxSpecses.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.JunBoxFactoryCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("线盒厂家", string.Join("，", rule.JunBoxFactories.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.BackPlanSpecsCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("背板规格", string.Join("，", rule.BackPlanSpecses.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.BackPlanFactoryCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("背板厂家", string.Join("，", rule.BackPlanFactories.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.AluFrameSpecsCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("铝框规格", string.Join("，", rule.AluFrameSpecses.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.AluFrameFactoryCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("铝框厂家", string.Join("，", rule.AluFrameFactories.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.BatterySpecsCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("电池规格", string.Join("，", rule.BatterySpecses.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.BatteryFactoryCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("电池厂家", string.Join("，", rule.BatteryFactories.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                if (rule.BatteryFactoryCheck)
                {
                    stack.Rules.Add(new KeyValuePair<string, string>("异常原因", string.Join("，", rule.BadReasons.Select(_ => string.IsNullOrWhiteSpace(_) ? "空" : _))));
                }
                stacks.Add(stack);
            }

            return stacks;
        }

        /// <summary>
        /// 组件回流
        /// </summary>
        /// <param name="model">拣货对象</param>
        public void Flowback(VSortRecord model)
        {
            var component = string.IsNullOrWhiteSpace(model.BarCode) ? null
                : mesDb.Components.AsNoTracking().FirstOrDefault(_ => _.BarCode == model.BarCode && _.Valid);
            if (component == null)
            {
                throw new MultiLanBizException(102012, model.BarCode);
            }
            else if (!string.IsNullOrWhiteSpace(component.PackNo))
            {
                throw new MultiLanBizException(141003, model.BarCode);//当前组件已打包，无法回流
            }

            var record = mesDb.SortRecords.AsNoTracking().FirstOrDefault(_ => _.BarCode == component.BarCode && _.Status == SortRecordStatus.Normal && _.Valid);
            if (record == null)
            {
                throw new MultiLanBizException(141004, model.BarCode);//组件尚未分拣，不可过站
            }
            else
            {
                var stack = mesDb.SortStacks.FirstOrDefault(_ => _.PkId == record.StackId && _.Valid);
                if (stack == null)
                {
                    throw new MultiLanBizException(141006, model.BarCode);//堆数据不存在
                }
                mesDb.SortRecords.Attach(record);
                record.Status = SortRecordStatus.Reset;
                record.ModifyBy = model.ModifyBy;
                record.ModifyTime = DateTime.Now;

                var topRecords = mesDb.SortRecords.AsNoTracking().Where(_ => _.LineId == record.LineId && _.StackNo == record.StackNo && _.PkId > record.PkId
                    && _.Status == SortRecordStatus.Normal && _.Valid).ToList().OrderBy(_ => _.PkId).ToList();
                if (!topRecords.Any()) //当前回流的组件是这一堆最后一块
                {
                    if (stack.SortNo <= 1)
                    {
                        stack.Valid = false;
                    }
                    else
                    {
                        stack.SortNo = stack.SortNo - 1;
                        stack.Status = SortStackStatus.Sorting;
                    }
                    stack.ModifyBy = model.ModifyBy;
                    stack.ModifyTime = DateTime.Now;
                    mesDb.SaveChanges();
                }
                else
                {
                    if (stack.Status == SortStackStatus.Sorting) //当前最后一堆正在分拣
                    {
                        foreach (var item in topRecords)
                        {
                            mesDb.SortRecords.Attach(item);
                            item.SortNo = item.SortNo - 1;
                            item.ModifyBy = model.ModifyBy;
                            item.ModifyTime = DateTime.Now;
                        }

                        stack.SortNo = stack.SortNo - 1;
                        stack.Status = SortStackStatus.Sorting;
                        stack.ModifyBy = model.ModifyBy;
                        stack.ModifyTime = DateTime.Now;
                        mesDb.SaveChanges();
                    }
                    else
                    {
                        var lastSortedRecord = topRecords.LastOrDefault();
                        if (lastSortedRecord.StackId != record.StackId) //最后一块分拣的不在这一堆：说明这一堆是完整的，这一块干掉，其他数据不动
                        {
                            //已经干掉了，这里保存就好
                            mesDb.SaveChanges();
                        }
                        else //最后一块分拣的在这一堆：说明这一堆实际正在分拣，后续组件连续减一
                        {
                            var curStackRecords = topRecords.Where(_ => _.StackId == record.StackId).ToList();
                            foreach (var item in curStackRecords)
                            {
                                mesDb.SortRecords.Attach(item);
                                item.SortNo = item.SortNo - 1;
                                item.ModifyBy = model.ModifyBy;
                                item.ModifyTime = DateTime.Now;
                            }

                            var otherStackRecords = topRecords.Where(_ => _.StackId != record.StackId).ToList();
                            //除了这一堆，还有新组建未成堆
                            if (otherStackRecords.Any())
                            {
                                var otherStackId = otherStackRecords.FirstOrDefault().StackId;
                                var otherStack = mesDb.SortStacks.FirstOrDefault(_ => _.PkId == otherStackId && _.Valid);
                                foreach (var item in otherStackRecords)
                                {
                                    mesDb.SortRecords.Attach(item);
                                    if (otherStackRecords.IndexOf(item) == 0) //第一块组件堆到前面一堆
                                    {
                                        item.SortNo = stack.FullCount;
                                        item.StackId = stack.PkId;
                                        item.ModifyBy = model.ModifyBy;
                                        item.ModifyTime = DateTime.Now;
                                    }
                                    else
                                    {
                                        item.SortNo = item.SortNo - 1;
                                        item.ModifyBy = model.ModifyBy;
                                        item.ModifyTime = DateTime.Now;
                                    }
                                }

                                if (!otherStackRecords.Any(_ => _.StackId == otherStack.PkId)) //后面一堆已经没有组件了，直接干掉
                                {
                                    otherStack.Valid = false;
                                    otherStack.ModifyBy = model.ModifyBy;
                                    otherStack.ModifyTime = DateTime.Now;
                                }
                                else // 后面一堆还有组件了，当前数量减一
                                {
                                    otherStack.SortNo = stack.SortNo - 1;
                                    otherStack.Status = SortStackStatus.Sorting;
                                    otherStack.ModifyBy = model.ModifyBy;
                                    otherStack.ModifyTime = DateTime.Now;
                                }
                            }
                            
                            mesDb.SaveChanges();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 清空堆
        /// </summary>
        /// <param name="model">堆对象</param>
        public void ClearStack(VSortStack model)
        {
            var stack = mesDb.SortStacks.AsNoTracking().FirstOrDefault(_ => _.MachineId == model.MachineId && _.StackNo == model.StackNo
                && _.Status == SortStackStatus.Sorting && _.Valid);
            if (stack == null)
            {
                throw new MultiLanBizException(141007, model.StackNo.ToString());//当前堆已经满托，无需清空
            }

            mesDb.SortStacks.Attach(stack);
            stack.Status = SortStackStatus.Finish;
            stack.ModifyBy = model.ModifyBy;
            stack.ModifyTime = DateTime.Now;
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 保存分拣规则
        /// </summary>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="machineId">设备ID</param>
        /// <param name="operator">操作人</param>
        /// <param name="rules">规则列表</param>
        public void SaveRules(string workshopCode, int machineId, int @operator, List<VSortRule> rules)
        {
            var workshop = mesDb.Workshops.AsNoTracking().FirstOrDefault(_ => _.WorkshopCode == workshopCode && _.Valid);
            var machine = mesDb.Machines.AsNoTracking().FirstOrDefault(_ => _.PkId == machineId && _.Valid);
            rules.ForEach(_ =>
            {
                _.CreateBy = @operator;
                _.CreateTime = DateTime.Now;

                _.WorkshopCode = workshop.WorkshopCode;
                _.WorkshopName = workshop.WorkshopName;
                _.LineId = machine.LineId;
                _.LineName = machine.LineName;
                _.MachineId = machine.PkId;
                _.MachineNo = machine.MachineNo;
                _.MachineName = machine.MachineName;
                _.OrderNo = JsonUtils.Serialize(_.OrderNos);
                _.WorkOrderNo = JsonUtils.Serialize(_.WorkOrderNos);
                _.NameplateModel = JsonUtils.Serialize(_.NameplateModels);
                _.PowerLevel = JsonUtils.Serialize(_.PowerLevels);
                _.CurrentLevel = JsonUtils.Serialize(_.CurrentLevels);
                _.QLevel = JsonUtils.Serialize(_.QLevels);
                _.ComponentColor = JsonUtils.Serialize(_.ComponentColors);
                _.JunBoxSpecs = JsonUtils.Serialize(_.JunBoxSpecses);
                _.JunBoxFactory = JsonUtils.Serialize(_.JunBoxFactories);
                _.BackPlanSpecs = JsonUtils.Serialize(_.BackPlanSpecses);
                _.BackPlanFactory = JsonUtils.Serialize(_.BackPlanFactories);
                _.AluFrameSpecs = JsonUtils.Serialize(_.AluFrameSpecses);
                _.AluFrameFactory = JsonUtils.Serialize(_.AluFrameFactories);
                _.BatterySpecs = JsonUtils.Serialize(_.BatterySpecses);
                _.BatteryFactory = JsonUtils.Serialize(_.BatteryFactories);
                _.BadReason = JsonUtils.Serialize(_.BadReasons.Select(b => b.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries)[0]));
                _.Status = SortRuleStatus.Valid;
            });

            var entities = mesDb.SortRules.AsNoTracking().Where(_ => _.WorkshopCode == workshopCode && _.MachineId == machine.PkId && _.Status == SortRuleStatus.Valid && _.Valid).ToList();
            var newStacks = rules.Where(_ => !entities.Select(e => e.StackNo).Contains(_.StackNo)).ToList();

            entities.ForEach(entity =>
            {
                var rule = rules.FirstOrDefault(_ => _.StackNo == entity.StackNo);
                mesDb.SortRules.Attach(entity);
                if (rule == null)
                {
                    //当前堆号删除
                    entity.Status = SortRuleStatus.invalid;
                    entity.ModifyBy = @operator;
                    entity.ModifyTime = DateTime.Now;
                }
                else
                {
                    //当前堆号存在
                    var modified = false;
                    foreach (var property in typeof(SortRule).GetProperties())
                    {
                        if (!new[] { "PkId", "Valid", "CreateBy", "CreateTime", "ModifyBy", "ModifyTime" }.Contains(property.Name, StringComparer.CurrentCultureIgnoreCase))
                        {
                            var value1 = Convert.ToString(property.GetValue(entity));
                            var value2 = Convert.ToString(typeof(VSortRule).GetProperty(property.Name).GetValue(rule));
                            if (!StringUtils.Equals(value1, value2))
                            {
                                modified = true;
                                break;
                            }
                        }
                    }

                    if (modified)
                    {
                        entity.Status = SortRuleStatus.invalid;
                        entity.ModifyBy = @operator;
                        entity.ModifyTime = DateTime.Now;
                        newStacks.Add(rule);
                    }
                }
            });

            if (newStacks.Any())
            {
                mesDb.SortRules.AddRange(Mapper.Map<List<SortRule>>(newStacks));
            }
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 保存护脚机配置
        /// </summary>
        /// <param name="model">护脚机配置</param>
        public void SaveFootProtech(VFootProtectConfig model)
        {
            var query = mesDb.FootProtectConfigs.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(model.WorkOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == model.WorkOrderNo);
            }
            else if (!string.IsNullOrWhiteSpace(model.OrderNo))
            {
                query = query.Where(_ => _.OrderNo == model.OrderNo);
            }
            else
            {
                query = query.Where(_ => _.OrderNo == "" && _.WorkOrderNo == "");
            }
            var entity = query.FirstOrDefault();

            if (entity != null)
            {
                mesDb.FootProtectConfigs.Attach(entity);
                entity.Valid = false;
                entity.ModifyBy = model.CreateBy;
                entity.ModifyTime = DateTime.Now;
            }

            model.FootProtectSortNos = string.Join(",", (model.FootProtectSortNos ?? string.Empty).Replace("，", ",").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(_ => Convert.ToInt32(_)));
            model.OverTurnSortNos = string.Join(",", (model.OverTurnSortNos ?? string.Empty).Replace("，", ",").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(_ => Convert.ToInt32(_)));
            model.CreateTime = DateTime.Now;
            mesDb.FootProtectConfigs.Add(Mapper.Map<FootProtectConfig>(model));
            mesDb.SaveChanges();
        }

        /// <summary>
        /// 获取护脚配置
        /// </summary>
        /// <param name="orderNo">订单号</param>
        /// <param name="workOrderNo">工单号</param>
        /// <returns>配置对象</returns>
        public VFootProtectConfig GetFootProtect(string orderNo, string workOrderNo)
        {
            var query = mesDb.FootProtectConfigs.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(workOrderNo))
            {
                query = query.Where(_ => _.WorkOrderNo == workOrderNo);
            }
            else if (!string.IsNullOrWhiteSpace(orderNo))
            {
                query = query.Where(_ => _.OrderNo == orderNo);
            }
            else
            {
                query = query.Where(_ => _.OrderNo == "" && _.WorkOrderNo == "");
            }
            var entity = query.FirstOrDefault();

            return Mapper.Map<VFootProtectConfig>(entity);
        }

        /// <summary>
        /// 获取组件包护脚配置
        /// </summary>
        /// <param name="barCode">组件条码</param>
        /// <returns>配置</returns>
        public VFootProtectConfig GetBarCodeFootProtectConfig(string barCode)
        {
            var component = mesDb.Components.AsNoTracking().FirstOrDefault(_ => _.BarCode == barCode && _.Valid);
            if (component == null)
            {
                throw new MultiLanBizException(110001, barCode);// "组件条码不存在"
            }
            var config = mesDb.FootProtectConfigs.AsNoTracking().FirstOrDefault(_ => _.WorkOrderNo == component.WorkOrderNo && _.Valid);
            if (config == null)
            {
                config = mesDb.FootProtectConfigs.AsNoTracking().FirstOrDefault(_ => _.OrderNo == component.OrderNo && _.WorkOrderNo == "" && _.Valid);
            }
            if (config == null)
            {
                config = mesDb.FootProtectConfigs.AsNoTracking().FirstOrDefault(_ => _.OrderNo == "" && _.WorkOrderNo == "" && _.Valid);
            }

            var model = Mapper.Map<VFootProtectConfig>(config);
            if (model != null)
            {
                model.FootProtectSortNoList = (model.FootProtectSortNos ?? string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(_ => Convert.ToInt32(_)).ToList();
                model.OverTurnSortNoList = (model.OverTurnSortNos ?? string.Empty).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(_ => Convert.ToInt32(_)).ToList();
            }

            return model;
        }

        /// <summary>
        /// 查询堆列表
        /// </summary>
        /// <param name="page">页码</param>
        /// <param name="limit">页面索引</param>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="lineId">流水线ID</param>
        /// <param name="stackNo">堆号</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <returns>堆列表，总数</returns>
        public Tuple<List<VSortStack>, int> QueryStacks(int page, int limit, string workshopCode, int lineId, int stackNo, DateTime? startTime, DateTime? endTime)
        {
            var query = mesDb.SortStacks.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(workshopCode))
            {
                query = query.Where(_ => _.WorkshopCode == workshopCode);
            }
            if (lineId > 0)
            {
                query = query.Where(_ => _.LineId == lineId);
            }
            if (stackNo > 0)
            {
                query = query.Where(_ => _.StackNo == stackNo);
            }
            if (startTime.HasValue && endTime.HasValue)
            {
                query = query.Where(_ => _.CreateTime >= startTime && _.CreateTime <= endTime.Value);
            }

            var count = query.Count();
            query = query.OrderByDescending(_ => _.PkId);
            var entities = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();
            var models = Mapper.Map<List<VSortStack>>(entities);
            return new Tuple<List<VSortStack>, int>(models, count);
        }

        /// <summary>
        /// 查询分拣记录
        /// </summary>
        /// <param name="stackId">堆ID</param>
        /// <returns>组件列表</returns>
        public List<VSortRecord> QuerySortRecords(int stackId)
        {
            var entities = mesDb.SortRecords.AsNoTracking().Where(_ => _.StackId == stackId && _.Status == SortRecordStatus.Normal && _.Valid).OrderBy(_ => _.SortNo).ToList();
            return Mapper.Map<List<VSortRecord>>(entities);
        }

        /// <summary>
        /// 查询分拣记录
        /// </summary>
        /// <param name="page">页码</param>
        /// <param name="limit">页面索引</param>
        /// <param name="workshopCode">车间代码</param>
        /// <param name="lineId">流水线ID</param>
        /// <param name="stackNo">堆号</param>
        /// <param name="startTime">开始时间</param>
        /// <param name="endTime">截止时间</param>
        /// <param name="barCode">组件条码</param>
        /// <param name="status">状态</param>
        /// <returns>分拣记录，总数</returns>
        public Tuple<List<VSortRecord>, int> QuerySortRecords(int page, int limit, string workshopCode, int lineId, int stackNo,
            DateTime? startTime, DateTime? endTime, string barCode, SortRecordStatus? status)
        {
            var query = mesDb.SortRecords.AsNoTracking().Where(_ => _.Valid);
            if (!string.IsNullOrWhiteSpace(barCode))
            {
                query = query.Where(_ => _.BarCode == barCode);
            }
            else
            {
                if (!string.IsNullOrWhiteSpace(workshopCode))
                {
                    query = query.Where(_ => _.WorkshopCode == workshopCode);
                }
                if (lineId > 0)
                {
                    query = query.Where(_ => _.LineId == lineId);
                }
                if (stackNo > 0)
                {
                    query = query.Where(_ => _.StackNo == stackNo);
                }
                if (startTime.HasValue && endTime.HasValue)
                {
                    query = query.Where(_ => _.CreateTime >= startTime && _.CreateTime <= endTime.Value);
                }
                if (status.HasValue)
                {
                    query = query.Where(_ => _.Status == status.Value);
                }
            }

            var count = query.Count();
            query = query.OrderByDescending(_ => _.PkId);
            var entities = limit <= 0 ? query.ToList() : query.Skip((page - 1) * limit).Take(limit).ToList();
            var models = Mapper.Map<List<VSortRecord>>(entities);
            return new Tuple<List<VSortRecord>, int>(models, count);
        }
    }
}
